package com.study.elasticsearchstudy.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.study.elasticsearchstudy.entity.Account;
import com.study.elasticsearchstudy.entity.MyResponse;
import com.study.elasticsearchstudy.service.AccountService;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * index 必须为纯小写 驼峰命名会报错
 * 例如:Invalid index name [myBank], must be lowercase
 * 属性可以存成驼峰,和java实体类保持一致,避免查询条件,查询结果,新增参数,修改参数等驼峰到下划线的转换
 * 这样就不存在mybatis里面结果集映射了, 数据库不加``字段大小写是不敏感的,所以只能用下划线
 * bank中的账号为下划线格式 account_number
 * mybank中的账号为驼峰格式accountNumber 一下操作都基于mybank操作
 */
@Slf4j
@RestController
@RequestMapping("/account")
public class AccountController {

    @Resource
    AccountService accountService;
    @Resource
    RestHighLevelClient restHighLevelClient;

    /**
     * 将bank里面的数据入库
     *
     * @throws IOException
     */
    @PostConstruct
    public void fromEs2Db() throws IOException {
        List<Account> accountList = accountService.selectList(new Account());
        if (accountList.size() > 0) {
            log.info("不需要初始化account数据");
            return;
        }
        log.info("开始初始化account数据");
        SearchRequest searchRequest = new SearchRequest("bank");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from(0);
        searchSourceBuilder.size(2000);
        searchRequest.source(searchSourceBuilder);

        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        System.out.println(response);
        List<Account> accounts = toAccountList(response);
        accountService.insertBatch(accounts);
        log.info("account数据初始化完毕");
    }

    @PostConstruct
    public void fromDb2es() {
        log.info("start");
        List<Account> accountList = accountService.selectList(new Account());
        BulkRequest bulkRequest = new BulkRequest("mybank");
        for (Account account : accountList) {
            // todo 这样存的数据有问题 什么问题?
//            bulkRequest.add(new IndexRequest().source(account, XContentType.JSON).id(String.valueOf(account.getAccountNumber())));
            bulkRequest.add(new IndexRequest().source(JSON.toJSONString(account), XContentType.JSON).id(String.valueOf(account.getAccountNumber())));
        }
        System.out.println(bulkRequest);
        try {
            restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        log.info("end");
    }

    /**
     * 新增
     * 先增db,再增es
     *
     * @param account
     * @return
     */
    @PostMapping("/account")
    public MyResponse insert(@RequestBody Account account) throws IOException {
        accountService.insert(account);
        log.info("db新增成功,新增的accountNumber:"+account.getAccountNumber());
        Integer accountNumber = account.getAccountNumber();
        IndexRequest indexRequest = new IndexRequest("mybank");
        indexRequest.id(String.valueOf(accountNumber));
        indexRequest.source(JSON.toJSONString(account), XContentType.JSON);
        IndexResponse index = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
        log.info("es新增成功 "+index);
        String msg = account.getMsg();
        if (StringUtils.isEmpty(msg)) {
            msg = "新增成功";
        }
        return MyResponse.success().setMessage(msg);
    }

    /**
     * 根据主键删除
     * 先删除db 在删除es
     *
     * @param id
     * @return
     */
    @DeleteMapping("/account/{id}")
    public MyResponse deleteByPrimaryKey(@PathVariable("id") Integer id) throws IOException {
        accountService.deleteByPrimaryKey(id);
        log.info("db删除成功");
        DeleteRequest deleteRequest = new DeleteRequest("mybank").id(String.valueOf(id));
        DeleteResponse response = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
        log.info("es 删除成功"+response);
        return MyResponse.success().setMessage("删除成功");
    }

    /**
     * 根据主键更新
     *
     * @param account
     * @return
     */
    @PutMapping("/account")
    public MyResponse update(@RequestBody Account account) throws IOException {
        accountService.update(account);
        log.info("db修改成功");
        UpdateRequest updateRequest = new UpdateRequest();
        updateRequest.index("mybank").id(String.valueOf(account.getAccountNumber()));
        // The number of object passed must be even but was [1]
        // 应该是key,value 例如:updateRequest.doc("lastname","张伞伞");
        // todo 必须是map吗? 不能是实体类吗
//        updateRequest.doc(JSON.parseObject(JSON.toJSONString(account), Map.class));
        updateRequest.doc(JSON.toJSONString(account),XContentType.JSON);
        UpdateResponse response = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
        log.info("es修改成功 "+response);
        String msg = account.getMsg();
        if (StringUtils.isEmpty(msg)) {
            msg = "修改成功";
        }
        return MyResponse.success().setMessage(msg);
    }

    /**
     * 根据主键查找
     *
     * @param id 主键
     * @return
     */
    @GetMapping("/account/{id}")
    public MyResponse selectByPrimaryKey(@PathVariable("id") Integer id) throws IOException {
        GetRequest getRequest = new GetRequest("mybank").id(String.valueOf(id));
        GetResponse response = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
        Map<String, Object> source = response.getSource();
        if (CollectionUtils.isEmpty(source)) {
            Account account = accountService.selectByPrimaryKey(id);
            log.info("数据来源于db");
            return MyResponse.success(account);
        }
        log.info("数据来源于es");
        return MyResponse.success(source);
    }

    /**
     * es版本 分页查询
     *
     * @param param
     * @return
     */
    @PostMapping("/getAccountListByPage")
    public MyResponse getAccountListByPage(@RequestBody Account param) throws IOException {
        Integer currentPage = param.getCurrentPage();
        Integer pageSize = param.getPageSize();
        SearchRequest searchRequest = new SearchRequest("mybank");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        termQuery("accountNumber", param.getAccountNumber(), boolQuery);
        termQuery("age", param.getAge(), boolQuery);
        termQuery("balance", param.getBalance(), boolQuery);
        matchQuery("firstname", param.getFirstname(), boolQuery);
        matchQuery("lastname", param.getLastname(), boolQuery);
        matchQuery("employer", param.getEmployer(), boolQuery);
        matchQuery("email", param.getEmail(), boolQuery);
        matchQuery("city", param.getCity(), boolQuery);
        matchQuery("address", param.getAddress(), boolQuery);
        matchQuery("state", param.getState(), boolQuery);
        matchQuery("gender", param.getGender(), boolQuery);
        searchSourceBuilder.query(boolQuery);
        searchSourceBuilder.from(currentPage * pageSize - pageSize);
        searchSourceBuilder.size(pageSize);
        searchSourceBuilder.sort("accountNumber");
        searchRequest.source(searchSourceBuilder);
        log.info(searchSourceBuilder.toString());
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        long total = response.getHits().getTotalHits().value;
        List<Account> accounts = toAccountList(response);
        PageInfo<Account> accountPageInfo = new PageInfo<>(accounts);
        accountPageInfo.setTotal(total);
        accountPageInfo.setPageSize(pageSize);
        return MyResponse.success(accountPageInfo);
    }

    private List<Account> toAccountList(SearchResponse response) {
        List<Account> accounts = new ArrayList<>();
        for (SearchHit hit : response.getHits().getHits()) {
            Map<String, Object> source = hit.getSourceAsMap();
            Account account = new JSONObject(source).toJavaObject(Account.class);
            accounts.add(account);
        }
        return accounts;
    }

    private void termQuery(String field, Integer value, BoolQueryBuilder boolQuery) {
        if (value != null) {
            boolQuery.must(QueryBuilders.termQuery(field, value));
        }
    }

    private void matchQuery(String field, String value, BoolQueryBuilder boolQuery) {
        if (StringUtils.hasLength(value)) {
            boolQuery.must(QueryBuilders.matchQuery(field, value));
        }
    }

    /**
     * mysql 版本分页查询
     *
     * @param param
     * @return
     */
    public MyResponse selectList(@RequestBody Account param) {
        Integer currentPage = param.getCurrentPage();
        Integer pageSize = param.getPageSize();
        PageHelper.startPage(currentPage, pageSize);
        List<Account> accounts = accountService.selectList(param);
        return MyResponse.success(new PageInfo<>(accounts));
    }

}
